package cn.dengta.webapp.trade.service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import javax.annotation.*;

import cn.dengta.common.context.Env;
import cn.dengta.common.model.*;
import cn.dengta.common.redis.RedisUtil;
import cn.dengta.common.web.Page;
import cn.dengta.context.model.RedisKey;
import cn.dengta.webapp.base.entity.BaseEntity;
import cn.dengta.webapp.base.service.BaseServiceImpl;
import cn.dengta.webapp.base.util.MybatisUtil;
import cn.dengta.webapp.trade.dao.UserLevelDao;
import cn.dengta.webapp.trade.entity.*;
import cn.dengta.webapp.trade.form.UserLevelQuery;
import cn.dengta.webapp.trade.model.UserLevelType;
import cn.dengta.webapp.user.entity.User;
import cn.dengta.webapp.user.entity.userLevel.UserLevelRule;
import cn.dengta.webapp.user.entity.userLevel.UserLevelRule.UserLevelRuleType;
import cn.dengta.webapp.user.entity.userLevel.VipRuleConfig;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import me.codeplayer.util.*;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import static cn.dengta.webapp.trade.entity.UserLevel.*;

/**
 * 用户等级 服务实现类
 *
 * @author lgy
 * @since 2022-11-28
 */
@Slf4j
@Service
public class UserLevelService extends BaseServiceImpl<UserLevel, UserLevelDao, Long> {

	@Resource
	UserLevelLogService userLevelLogService;
	@Resource
	UserEquityBagService userEquityBagService;
	@Resource
	UserBagAvatarService userBagAvatarService;
	@Resource
	RedisTemplate<String, Object> redisTemplate;

	public Page<UserLevel> findPage(Page<UserLevel> page, UserLevelQuery query, TimeInterval addTimeInterval) {
		QueryWrapper<UserLevel> wrapper = new QueryWrapper<>();
		final String prefix = "log.";
		if (query != null) {
			wrapper.eq(query.getUserId() != null, prefix + ID, query.getUserId());
			wrapper.eq(query.getLevel() != null, prefix + LEVEL, query.getLevel());
			wrapper.eq(StringUtil.notEmpty(query.getUserNickname()), "u.nickname", query.getUserNickname());
			wrapper.eq(query.getAgentId() != null, "u.agent_id", query.getAgentId());
		}
		MybatisUtil.wrapperInterval(wrapper, prefix + ADD_TIME, addTimeInterval);
		wrapper.orderByDesc(prefix + ID);
		return baseDao.findPage(page, wrapper);
	}

	/**
	 * 根据规则类型，计算相应任务
	 */
	public int change(@Nonnull UserLevelRuleType ruleType, @Nullable UserLevelRule rule, BaseEntity assocObj, User user, Object amount, Date bizTime) {
		if (UserLevel.disabled()) {
			return 0;
		}
		if (rule == null) {
			rule = baseDao.getByRuleType(ruleType);
		}

		boolean ruleEnabled = rule != null;
		if (!ruleEnabled && ruleType.innerSet) {
			ruleEnabled = true;
			rule = new UserLevelRule().init(ruleType.getLabel(), ruleType, (Integer) amount, bizTime, null);
		}
		if (rule == null || !rule.isActivity(bizTime.getTime())) {
			return 0;
		}
		final UserLevelRule finalRule = rule;
		final boolean finalRuleEnabled = ruleEnabled;
		Integer changedExp = RedisUtil.loadInLock(RedisKey.USER_LEVEL_LOCK_PREFIX + user.getId(), 30_000L, () -> {
			Integer changeExp = 0;
			final Long userId = user.getId();
			if (finalRuleEnabled) {
				final Integer minExp = finalRule.getMinExp();
				if (!ruleType.innerSet) {
					final TimeInterval addTimeInterval = finalRule.getType().getTimeInterval(bizTime);
					final int existCount = userLevelLogService.getCountOfLog(userId, ruleType, addTimeInterval);
					if (existCount >= finalRule.getMaxLimit()) { // 查询限制次数
						return changeExp;
					}
					if (existCount > 0 && ruleType.repeatCheck && assocObj != null && userLevelLogService.getCountOfLog(userId, ruleType, assocObj, addTimeInterval) > 0) {
						return changeExp;
					}
				}
				// 根据规则，计算相应任务。
				switch (ruleType) {
					case BUY_VIP: // 购买VIP
						changeExp = 0;
						VipRuleConfig cfg = finalRule.getResolvedCfg();
						if (cfg != null) {
							VipRuleConfig.VipConfigEntry entry = cfg.getEntry((Integer) amount, false);
							if (entry != null) {
								changeExp = entry.getExp();
							}
						}
						break;
					case LOGIN_IN: // 登录
					case MOMENT_PUSH: // 发布动态
					case REPLY_GREET: // 回复搭讪
					case LIVE_CHAT: // 直播发言
					case FOLLOW_ANCHOR: // 关注主播
					case UPLOAD_AVATAR: // 上传头像
					case BIND_PHONE: // 绑定手机
					case BIND_FACE_BOOK: // 绑定FaceBook
					case FIRST_RECHARGE: // 首次充值
					case CALL_EVAL: // 通话评价
						changeExp = minExp;
						break;
					case USE_COIN: // 消耗钻石
					case RECHARGE_COIN: // 充值钻石
					case USE_GOLD: // 消耗金币
						int coin = NumberUtil.getInt(amount, 0);
						if (coin < 100) {
							return 0;
						}
						changeExp = minExp * (coin / 100);
						break;
					case LIVE: // 直播
						int minute = NumberUtil.getInt(TimeUnit.SECONDS.toMinutes((Long) amount), 0); // 分钟
						final int factor = Env.inProduction() ? 60 : 1;
						if (minute < factor) {
							return 0;
						}
						changeExp = minExp * (minute / factor);
						break;
				}
			}
			if (changeExp > 0) {
				final UserLevel account = getByIdForUpdate(userId);
				final Long oldExp = account.getExp();
				final long aLLExp = oldExp + changeExp;
				final Integer newLevel = UserLevelType.calcLevel(aLLExp).getLeft();
				final Integer oldLevel = account.getLevel();
				log.info("用户等级{}：经验{} -> {},等级{} -> {}", userId, oldExp, aLLExp, oldLevel, newLevel);
				account.setExp(aLLExp).setLevel(newLevel);
				final Pair<Integer, Integer> pair = UserLevelType.calcAvatarRangeLevelByExp(oldExp, aLLExp); // 头像框
				if (pair != null) {
					String alias = UserBagAvatar.BagAvatarName.USER_LEVEL.alias;
					List<UserBagAvatar> aliasByCache = userBagAvatarService.findAliasByCache(false, alias);
					if (X.isValid(aliasByCache)) {
						Map<String, UserBagAvatar> userBagAvatarMap = CollectionUtil.toHashMap(aliasByCache, UserBagAvatar::getName);
						long flag;
						UserBagAvatar bagAvatar;
						for (int i = pair.getLeft(); i <= pair.getRight(); i++) {
							flag = 1L << (i - 1);
							if (account.hasFlag(flag)) {
								continue;
							}
							bagAvatar = userBagAvatarMap.get("userLv" + i);
							if (Bean.hasId(bagAvatar) && bagAvatar.getAlias().equals(alias)) {
								userEquityBagService.giveAvatar(bagAvatar.idStr(), userId);
								account.addFlag(flag);
							}
						}
					}
				}
				final boolean success = update(account) > 0;
				// 新增任务变更记录。
				final UserLevelLog accountLog = new UserLevelLog().init(userId, ruleType, changeExp, oldExp, aLLExp, assocObj, bizTime);
				accountLog.setStatus(Status.valueOf(success));
				userLevelLogService.saveOrUpdate(accountLog);
				addUserLevelExp(userId, aLLExp);
			}
			return changeExp;
		});

		return changedExp == null ? 0 : changedExp;

	}

	/**
	 * 增加用户等级
	 */
	public void addUserLevelExp(Long userId, long exp) {
		if (exp > 0) {
			redisTemplate.opsForZSet().add(RedisKey.USER_LEVEL_EXP, userId.toString(), exp);
		}
	}

	/**
	 * 查询redis用户经验等级
	 */
	public Integer getLevelOfRedis(Long userId) {
		ZSetOperations<String, Object> opsForZSet = redisTemplate.opsForZSet();
		final String uIdStr = userId.toString();
		Double exp = opsForZSet.score(RedisKey.USER_LEVEL_EXP, uIdStr);
		if (exp == null) {
			exp = NumberUtil.getDouble(X.map(get(userId), UserLevel::getExp), 0);
			if (exp > 0) {
				opsForZSet.add(RedisKey.USER_LEVEL_EXP, uIdStr, exp);
			}
		}
		return UserLevelType.calcLevel(exp.longValue()).getLeft();
	}

	@Override
	public UserLevel getByIdForUpdate(Long id) {
		return getById(id, true);
	}

	@Override
	public UserLevel get(Long id) {
		return getById(id, false);
	}

	private UserLevel getById(@Nonnull Long userId, boolean forUpdate) {
		UserLevel t = forUpdate ? super.getByIdForUpdate(userId) : super.get(userId);
		if (t != null) {
			return t;
		}
		final Date now = new Date();
		return forUpdate ? initAccount(userId, now) : new UserLevel().init(userId, now);
	}

	public UserLevel initAccount(@Nonnull Long userId, Date now) {
		UserLevel t = new UserLevel().init(userId, now);
		save(t);
		return t;
	}

}
